#ifndef ROBOTACTION_H
#define ROBOTACTION_H

#include <QParallelAnimationGroup>
#include <QString>
#include <QVector>

#include "robot.h"
#include "robotmotiontrack.h"
#include "soundtrack.h"
#include "facialtrack.h"

/**
 * @brief Animates the robot.
 *
 * A robot animation has two components: the motion and the sound.  They are stored
 * as "tracks", analogous to the tracks used for almost any media editing software.
 * These tracks inherit from QAbstractAnimation, so that this action can "play" them
 * in parallel.  The motion track is itself a parallel group of animations, one
 * for each joint of the robot.
 *
 * The motion track and each joint subtrack of the animation can have key frames set
 * at certain timestamps to govern the animation.  Being able to set the joint
 * key frames independent from each other is very flexible and powerful, but our
 * current action file format isn't written with that ability in mind (see the
 * ActionFileWriter class for a brief explanation).  The set of "active" joints is
 * defined as the joints that have one or more key frames.
*/
class RobotAction : public QParallelAnimationGroup
{
    Q_OBJECT

private:
    QString _name;
    Robot* _robot;

    RobotMotionTrack* _motionTrack;
    RobotFacialTrack* _facialTrack;
    SoundTrack* _soundTrack;

protected:
    void updateCurrentTime(int currentTime);
    void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState);

public:
    //Constructors
    explicit RobotAction(Robot* robot, QString name = "New Action", QObject *parent = 0);
    //Destructor
    ~RobotAction();

    void setName(QString name);
    //void setDuration(int msec);

    //Points the action at a different robot (may cause glitches until another keyframe is hit,
    //may not. It tries to seek to the current position at any rate.)
    void setRobot(Robot* robot);
    Robot* getRobot() const{return _robot;}

    void startBatch();
    void endBatch();

    //Functions for information about the action
    QString name() const;
    QList<int> jointIDs() const;
    QList<int> activeJointIDs() const;

    //Motion key frame functions
    void addKeyFrame(MotionKeyFrame frame);
    void setStartValue(int jointID, float theta);
    void setEndValue(int jointID, float theta);
    MotionKeyFrame insertKeyFrameAt(int msec);
    float insertKeyFrameAt(int msec, int jointID);
    float setKeyFrameAt(int msec, int jointID, float theta);
    MotionKeyFrame deleteKeyFrameAt(int msec);
    float deleteKeyFrameAt(int msec, int jointID);
    void moveKeyFrame(int msec, int newMsec);
    void moveKeyFrame(int msec, int jointID, int newMsec);
    void setMotionDuration(int msec);

    //Functions for information about the motion track and key frames
    int motionDuration() const;
    float keyValueAt(int msec, int jointID) const;
    QList<MotionKeyFrame> keyFrames() const;

    /* Not implement
    QList<QVector3D> rightHandKeyFrames() const;
    QList<QVector3D> leftHandKeyFrames() const;
    */

    //Facial expression functions
    void setStartExpression(QString filename);
    void setEndExpression(QString filename);
    void setExpressionAt(int msec, QString filename);
    void moveExpression(int msec, int newMsec);
    void deleteExpression(int msec);
    void setFacialTrackDuration(int msec);

    FacialExpressionInfo expressionAt(int msec) const;
    QList<FacialExpressionInfo> facialInformation() const;

    //Sound Frame functions
    void setSoundAt(int msec, QString fileName);
    SoundInfo deleteSound(int msec);
    void moveSound(int msec, int newMsec);
    int trimSound(int msec, int frontTrim, int backTrim);
    void setSoundTrackDuration(int msec);

    //Functions for information about the soundtrack
    SoundInfo soundAt(int msec) const;
    QList<SoundInfo> soundInformation() const;

    //Functions for serializing a robot action.  They are declared
    //as friends to allow easy access to the action and its members.
    friend QTextStream &operator<<(QTextStream &out, const RobotAction &action);
    friend QDataStream &operator<<(QDataStream &out, const RobotAction &action);

    //Allow the GUI timeline to have friendly access to the sound track and motion track
    friend class GUIActionTimeline;

public slots:
    //Functions for additional playback control
    void skip(int msec);
    void nextKeyFrame();
    void prevKeyFrame();

private slots:
    //Called by the motion track when key frames are hit
    void onKeyFrame(int jointID, int msec);
    void onKeyFrame(int msec);

    //Called by the motion track when key frames are modified
    void onKeyFrameCreated(int msec);
    void onKeyFrameSet(int msec, int jointID, float theta);
    void onKeyFrameDeleted(int msec);
    void onKeyFrameDeleted(int msec, int jointID);
    void onKeyFrameMoved(int msec, int newMsec);
    void onKeyFrameMoved(int msec, int jointID, int newMsec);

    //Called by the motion track when the duration changes
    void onMotionDurationChanged(int newMsec, int oldMsec);

    //Called by the sound track for various events
    void onSoundPlayed(int msec, QString name);
    void onSoundFinished(int msec, QString name);
    void onSoundDurationChanged(int msec, int newDuration);

    void onSoundCreated(int msec, QString name);
    void onSoundSet(int msec, QString name);
    void onSoundDeleted(int msec);
    void onSoundMoved(int msec, int newMsec);

signals:
    void timeChanged(int msec);

    //Signals regarding key frames and motion.  Essentially reroutes
    //the signals from the motion track
    void motionDurationChanged(int newMsec, int oldMsec);
    void keyFrame(int jointID, int msec);
    void keyFrame(int msec);
    //Signals regarding key frame creation/manipulation/deletion
    void keyFrameCreated(int msec);
    void keyFrameSet(int msec, int jointID, float theta);
    void keyFrameDeleted(int msec);
    void keyFrameDeleted(int msec, int jointID);
    void keyFrameMoved(int msec, int newMsec);
    void keyFrameMoved(int msec, int jointID, int newMsec);

    //Signals regarding sounds
    void soundPlayed(int msec, QString name);
    void soundFinished(int msec, QString name);
    void soundDurationChanged(int msec, int newDuration);
    //Signals regarding sound creation/manipulation/deletion
    void soundCreated(int msec, QString name);
    void soundSet(int msec, QString name);
    void soundDeleted(int msec);
    void soundMoved(int msec, int newMsec);
};

#endif // ROBOTACTION_H
